home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Programming / DiceSource / src / vopts / reader.c < prev    next >
C/C++ Source or Header  |  1997-09-09  |  17KB  |  499 lines

  1. /*
  2.  *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
  3.  *    use is allowed under the terms of the DICE-LICENSE FILE,
  4.  *    DICE-LICENSE.TXT.
  5.  */
  6. #include "vopts.h"
  7.  
  8. Prototype int get_simple_token(char *buf);
  9. Prototype int get_token(char *buf1, char *buf2, char *buf3);
  10. Prototype struct G_OBJECT **newobj(struct G_OBJECT **objlist, int class, char *buf);
  11. Prototype int parse_config(char * cfname);
  12. Prototype int init_config(char *cfname);
  13. Prototype void close_config(void);
  14.  
  15. /*********************************************************************
  16.  * TOKENS:
  17.  *      TOK_EOF     End of file
  18.  *                  '/''*' '*''/'
  19.  *      TOK_STRING  "string"
  20.  *      TOK_STRING  'string'
  21.  *      TOK_BAR     BAR
  22.  *      TOK_TEXT    TEXT   <string>
  23.  *      TOK_BUTTON  BUTTON <string>
  24.  *      TOK_CHECK   CHECK  <string> <string> <string>
  25.  *      TOK_CYCLE   CYCLE  <string>
  26.  *      TOK_GROUP   GROUP  <string>
  27.  *      TOK_LGROUP  LGROUP <string>
  28.  *      TOK_ITEM    ITEM   <string> <string>
  29.  *      TOK_LIST    LIST   <string> <string> <string>
  30.  *      TOK_MENU    MENU   <string>
  31.  *      TOK_STRING  STRING <string> <string>
  32.  *      TOK_TITLE   TITLE  <string>
  33.  *      TOK_VALUE   VALUE  <string> <string>
  34.  */
  35. #define TOK_ERROR  -1
  36. #define TOK_EOF     0 /* Note ordering of ERROR/EOF is assumed for easy tests below */
  37. #define TOK_STRTOK  1
  38. #define TOK_TOKEN   2
  39. #define TOK_BAR     3
  40. #define TOK_BUTTON  4
  41. #define TOK_CHECK   5
  42. #define TOK_CYCLE   6
  43. #define TOK_GROUP   7
  44. #define TOK_LGROUP  8
  45. #define TOK_ITEM    9
  46. #define TOK_LIST   10
  47. #define TOK_MENU   11
  48. #define TOK_STRING 12
  49. #define TOK_TITLE  13
  50. #define TOK_VALUE  14
  51. #define TOK_TEXT   15
  52.  
  53. struct TOK_LOOK {
  54.    char toktype;   /* Value to return for this token                        */
  55.    char token[6];  /* Note that we only allow 6 characters in a token       */
  56.    char strings;   /* Number of strings that the token takes as an argument */
  57. };
  58.  
  59. struct TOK_LOOK tokens[] = {
  60.    { TOK_BAR,     "BAR   ", 0},
  61.    { TOK_BUTTON,  "BUTTON", 1},
  62.    { TOK_CHECK,   "CHECK ", 3},
  63.    { TOK_CYCLE,   "CYCLE ", 1},
  64.    { TOK_GROUP,   "GROUP ", 1},
  65.    { TOK_LGROUP,  "LGROUP", 1},
  66.    { TOK_ITEM,    "ITEM  ", 3},
  67.    { TOK_LIST,    "LIST  ", 2},
  68.    { TOK_MENU,    "MENU  ", 1},
  69.    { TOK_STRING,  "STRING", 2},
  70.    { TOK_TITLE,   "TITLE ", 1},
  71.    { TOK_VALUE,   "VALUE ", 2},
  72.    { TOK_TEXT,    "TEXT  ", 1},
  73. };
  74. #define MAX_LOOK (sizeof(tokens)/sizeof(struct TOK_LOOK))
  75.  
  76. /*********************************************************************
  77.  * Character classes:
  78.  *  0  CL_EOF    - EOF
  79.  *  1  CL_SLASH  - /
  80.  *  2  CL_STAR   - *
  81.  *  3  CL_DQUOTE - "
  82.  *  4  CL_SQUOTE - '
  83.  *  5  CL_ALPHA  - A-Z a-z
  84.  *  6  CL_BLANK  - ' ' \t \n
  85.  *  7  CL_OTHER  - Anything else
  86.  */
  87. #define CL_EOF     0
  88. #define CL_SLASH   1
  89. #define CL_STAR    2
  90. #define CL_DQUOTE  3
  91. #define CL_SQUOTE  4
  92. #define CL_ALPHA   5
  93. #define CL_BLANK   6
  94. #define CL_OTHER   7
  95. #define MAX_CL     8
  96.  
  97. /*********************************************************************
  98.  * States:
  99.  *  0  ST_SCN - SCAN   - Scanning - Looking for any character - skipping white space
  100.  *  1  ST_GSL - GOTSL  - Found /  - Checking for a matching * to start a comment
  101.  *  2  ST_CMT - CMT    - Comment  - Looking for a * to close a comment
  102.  *  3  ST_GST - GOTST  - Found *  - Checking for a / to close a comment
  103.  *  4  ST_DQT - DQUOTE - String " - Looking for a matching "
  104.  *  5  ST_SQT - SQUOTE - String ' - Looking for a matching '
  105.  *  6  ST_TOK - TOKEN  - Token    - Gathering a keyword token
  106.  */
  107. #define ST_SCN  0
  108. #define ST_GSL  1
  109. #define ST_CMT  2
  110. #define ST_GST  3
  111. #define ST_DQT  4
  112. #define ST_SQT  5
  113. #define ST_TOK  6
  114. #define MAX_ST 7
  115. #define MASK_ST 7
  116.  
  117. /*********************************************************************
  118.  * State Table Transitions:
  119.  *
  120.  *            Character Class
  121.  *            0-EOF   1-/      2-*      3-"      4-'      5-A-Z      6-Blank  7-Other
  122.  *   State
  123.  *   0 SCAN    END    GOTSL   ERROR    DQUOTE   SQUOTE    >TOKEN     SCAN     ERROR
  124.  *   1 GOTSL  ERROR   ERROR   CMT      ERROR    ERROR     ERROR      ERROR    ERROR
  125.  *   2 CMT    ERROR   CMT     GOTST    CMT      CMT       CMT        CMT      CMT
  126.  *   3 GOTST  ERROR   SCAN    CMT      CMT      CMT       CMT        CMT      CMT
  127.  *   4 DQUOTE ERROR   >DQUOTE >DQUOTE  !SCAN    >DQUOTE   >DQUOTE    >DQUOTE  >DQUOTE
  128.  *   5 SQUOTE ERROR   >SQUOTE >SQUOTE  >SQUOTE  !SCAN     >SQUOTE    >SQUOTE  >SQUOTE
  129.  *   6 TOKEN  +END    +GOTSL  ERROR    +DQUOTE  +SQUOTE   >TOKEN     +SCAN    ERROR
  130.  * Note: >     - AC_SAV - Means to append the current character and continue scan
  131.  *       !     - AC_STR - Means to return the current token as a string.
  132.  *       +     - AC_TOK - Means to return the current token as a keyword
  133.  *       ERROR - AC_ERR - Indicates issuing an error
  134.  *       END   - AC_END - Indicates returing the END token.
  135.  */
  136. #define AC_SKP (0<<5) /* Must be zero - the default to do nothing */
  137. #define AC_SAV (1<<5)
  138. #define AC_STR (2<<5)
  139. #define AC_TOK (3<<5)
  140. #define AC_ERR (4<<5)
  141. #define AC_END (5<<5)
  142. #define MASK_AC (7<<5)
  143.  
  144. char statetab[MAX_ST][MAX_CL] =
  145. {
  146. /* 0 ST_SCN  */
  147.    { AC_END,        ST_GSL,         AC_ERR,         ST_DQT,
  148.      ST_SQT,        AC_SAV|ST_TOK,  ST_SCN,         AC_ERR         },
  149. /* 1 ST_GSL */
  150.    { AC_ERR,        AC_ERR,         ST_CMT,         AC_ERR,
  151.      AC_ERR,        AC_ERR,         AC_ERR,         AC_ERR         },
  152. /* 2 ST_CMT   */
  153.    { AC_ERR,        ST_CMT,         ST_GST,         ST_CMT,
  154.      ST_CMT,        ST_CMT,         ST_CMT,         ST_CMT         },
  155. /* 3 ST_GST */
  156.    { AC_ERR,        ST_SCN,         ST_CMT,         ST_CMT,
  157.      ST_CMT,        ST_CMT,         ST_CMT,         ST_CMT         },
  158. /* 4 ST_DQT*/
  159.    { AC_ERR,        AC_SAV|ST_DQT,  AC_SAV|ST_DQT,  AC_STR|ST_SCN,
  160.      AC_SAV|ST_DQT, AC_SAV|ST_DQT,  AC_SAV|ST_DQT,  AC_SAV|ST_DQT  },
  161. /* 5 ST_SQT*/
  162.    { AC_ERR,        AC_SAV|ST_SQT,  AC_SAV|ST_SQT,  AC_SAV|ST_SQT,
  163.      AC_STR|ST_SCN, AC_SAV|ST_SQT,  AC_SAV|ST_SQT,  AC_SAV|ST_SQT  },
  164. /* 6 ST_TOK */
  165.    { AC_TOK|ST_SCN, AC_TOK|ST_GSL,  AC_ERR,         AC_TOK|ST_DQT,
  166.      AC_TOK|ST_SQT, AC_SAV|ST_TOK,  AC_TOK|ST_SCN,  AC_ERR         },
  167. };
  168.  
  169. /*
  170.  * Get a simple token from the file.
  171.  * No parsing of semantics is done at this level.
  172.  */
  173. int get_simple_token(char *buf)
  174. {
  175.    int c;
  176.    int pos;
  177.    int class;
  178.    int action;
  179.  
  180.    pos = 0;
  181.  
  182.    for(;;)
  183.    {
  184.       /* Get the next character from the input file and assign a character class */
  185.       c = getc(global.fp);
  186.       class = CL_OTHER;
  187.       switch(c)
  188.       {
  189.          case EOF:  class = CL_EOF;    break;
  190.          case '/':  class = CL_SLASH;  break;
  191.          case '*':  class = CL_STAR;   break;
  192.          case '"':  class = CL_DQUOTE; break;
  193.          case '\'': class = CL_SQUOTE; break;
  194.          case '\n': global.line++;
  195.          case ' ':
  196.          case '\t':  class = CL_BLANK;  break;
  197.          default:
  198.             if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
  199.                class = CL_ALPHA;
  200.             break;
  201.       }
  202.       /* Run us through the state table to get an action and a new state */
  203.       action = statetab[global.state][class];
  204.       global.state = (action & MASK_ST);
  205.  
  206.       /* Perform the work for the action */
  207.       switch(action & MASK_AC)
  208.       {
  209.          case AC_SAV: if (pos < 64) /* make sure we don't overflow the buffer */
  210.                       {
  211.                          buf[pos++] = c;  /* Save the character */
  212.                          break;
  213.                       }
  214.                       /* Else fall through to the error case */
  215.          case AC_ERR: return(TOK_ERROR);
  216.          case AC_STR: buf[pos] = 0;   /* Null terminate what we gathered */
  217.                       return(TOK_STRTOK);
  218.          case AC_TOK: buf[pos] = 0;
  219.                       return(TOK_TOKEN);
  220.          case AC_END: return(TOK_EOF);
  221.       }
  222.    }
  223. }
  224.  
  225. /*
  226.  * Get the next token and any associated strings.
  227.  */
  228. int get_token(char *buf1,
  229.               char *buf2,
  230.               char *buf3
  231.              )
  232. {
  233.    int i;
  234.    int j;
  235.    int toktype;
  236.    int len;
  237.    char *strs[3];
  238.  
  239.    strs[0] = buf1;
  240.    strs[1] = buf2;
  241.    strs[2] = buf3;
  242.  
  243.    toktype = get_simple_token(buf1);
  244.    if (toktype == TOK_EOF)   return(toktype);
  245.    if (toktype != TOK_TOKEN) return(TOK_ERROR);
  246.  
  247.    /* Go through and uppercase the string */
  248.    /* we KNOW that it consists of only upper/lower case letters */
  249.    len = strlen(buf1);
  250.    if (len > 6) return(TOK_ERROR);
  251.    strcpy(buf1+len, "     "); /* Make sure we blank pad for the memcmp */
  252.    for(i = 0; i < 6; i++)
  253.       if (buf1[i] >= 'a') buf1[i] -= ('a'-'A');
  254.  
  255.    /* Now go through the table and look for a token */
  256.    for (i = 0; i < MAX_LOOK; i++)
  257.       if (!memcmp(buf1, tokens[i].token, 6)) break;
  258.    if (i == MAX_LOOK) return(TOK_ERROR);
  259.  
  260.    /* We have a matching token, get any strings that it wants */
  261.    for(j = 0; j < tokens[i].strings; j++)
  262.    {
  263.       toktype = get_simple_token(strs[j]);
  264.       if (toktype != TOK_STRTOK) return(TOK_ERROR);
  265.    }
  266.  
  267.    /* Everything checks out, let them know what type of token they got */
  268.    return((int)tokens[i].toktype);
  269. }
  270.  
  271. void init_group(struct G_OBJECT *object)
  272. {
  273.    while(object != NULL)
  274.    {
  275.       if (object->next) object->next->prev = object;
  276.       if (object->class == CLASS_CYCLE)
  277.       {
  278.          struct G_VALUE *val;
  279.          struct G_CYCLE *cyc;
  280.  
  281.          cyc = (struct G_CYCLE *)object;
  282.  
  283.          cyc->curval = cyc->values;
  284.          for(val = cyc->curval; val; val = val->next)
  285.          {
  286.             if (cyc->base.next) cyc->base.next->prev = (struct G_OBJECT *)cyc;
  287.          }
  288.       }
  289.       object = object->next;
  290.    }
  291. }
  292. /*
  293.  * Allocate an object structure to hold an entry
  294.  */
  295. static char sizetab[] = { 0,
  296.                           sizeof(struct G_STRING),
  297.                           sizeof(struct G_CYCLE),
  298.                           sizeof(struct G_CHECK),
  299.                           sizeof(struct G_GROUP),
  300.                           sizeof(struct G_LIST)
  301.                          };
  302.  
  303. struct G_OBJECT **newobj(struct G_OBJECT **objlist,
  304.                          int class,
  305.                          char *buf
  306.                         )
  307. {
  308.    struct G_OBJECT *obj;
  309.  
  310.    obj = get_mem(sizetab[class]);
  311.    if (obj == NULL) return(NULL);
  312.  
  313.    if (objlist) *objlist = obj;
  314.    obj->class = class;
  315.    obj->title = savestr(buf);
  316.    return(&obj->next);
  317. }
  318.  
  319. /*
  320.  * Parse the configuration file
  321.  */
  322. int parse_config(char *cfname)
  323. {
  324.    static char buf1[66], buf2[66], buf3[66];
  325.    int toktype;
  326.    int menupos;
  327.    int buttonpos;
  328.    int groupcnt;
  329.    int listflag;
  330.    int textpos;
  331.    struct G_OBJECT **objlist;
  332.    struct G_GROUP  **grplist;
  333.    struct G_VALUE  **valent;
  334.    struct G_GROUP  *group;
  335.  
  336.    grplist = &global.groups;
  337.    objlist = &global.objects;
  338.    valent = NULL;
  339.  
  340.    textpos = menupos = buttonpos = 0;
  341.    groupcnt = 0;
  342. /*
  343.    listflag = 0;
  344. */
  345.    listflag = 1;
  346.    if (init_config(cfname)) return(2);
  347.  
  348.    while((toktype = get_token(buf1, buf2, buf3)) > TOK_EOF)
  349.    {
  350.       switch(toktype)
  351.       {
  352.          case TOK_TEXT:   global.text[textpos] = savestr(buf1);
  353.                           if (textpos++ > MAX_TEXT) goto error;
  354.                           break;
  355.          case TOK_BAR:    global.menuitem[menupos].nm_Type   = MENU_ITEM;
  356.                           global.menuitem[menupos].nm_Label  = NM_BARLABEL;
  357.                           if (menupos++ > MAX_MENU) goto error;
  358.                           break;
  359.          case TOK_MENU:   global.menuitem[menupos].nm_Type   = MENU_MENU;
  360.                           global.menuitem[menupos].nm_Label  = savestr(buf1);
  361.                           if (menupos++ > MAX_MENU) goto error;
  362.                           break;
  363.          case TOK_ITEM:   global.menuitem[menupos].nm_Type   = MENU_ITEM;
  364.                           global.menuitem[menupos].nm_Label  = savestr(buf1);
  365.                           if (buf2[0])
  366.                              global.menuitem[menupos].nm_CommKey = savestr(buf2);
  367.                           global.menuitem[menupos].nm_UserData = savestr(buf3);
  368.                           if (menupos++ > MAX_MENU) goto error;
  369.                           break;
  370.          case TOK_BUTTON: if (buttonpos > MAX_BUTTON) goto error;
  371.                           global.button[buttonpos++].title = savestr(buf1);
  372.                           break;
  373.          case TOK_TITLE:  strcpy(global.wtitle, buf1);
  374.                           break;
  375.          case TOK_LGROUP:
  376.          case TOK_GROUP:  grplist = (struct G_GROUP **)
  377.                                     newobj(
  378.                                            (struct G_OBJECT **)grplist,
  379.                                            CLASS_GROUP, buf1);
  380.                           if (!grplist) return(1);
  381.                           objlist = &(((struct G_GROUP *)grplist)->objects);
  382.                           valent = NULL;
  383. /*
  384.                           (*grplist)->local = toktype == TOK_LGROUP ? 1 : 0;
  385. */
  386.                           ((struct G_GROUP *)grplist)->local
  387.                                             = (toktype == TOK_LGROUP ? 1 : 0);
  388.                           if (groupcnt > global.maxsize) global.maxsize = groupcnt;
  389.                           groupcnt = 2;
  390.                           break;
  391.          case TOK_STRING: objlist = newobj(objlist, CLASS_STRING, buf1);
  392.                           if (!objlist) return(1);
  393.                           groupcnt++;
  394.                           ((struct G_STRING *)objlist)->option = savestr(buf2);
  395.                           valent = NULL;
  396.                           break;
  397.          case TOK_CHECK:  objlist = newobj(objlist, CLASS_CHECK, buf1);
  398.                           if (!objlist) return(1);
  399.                           groupcnt++;
  400.                           ((struct G_CHECK *)objlist)->option0 = savestr(buf2);
  401.                           ((struct G_CHECK *)objlist)->option1 = savestr(buf3);
  402.                           valent = NULL;
  403.                           break;
  404.          case TOK_LIST:   objlist = newobj(objlist, CLASS_LIST, buf1);
  405.                           if (!objlist) return(1);
  406.                           groupcnt += 3;
  407.                           /* lists are just a bit bigger than three lines,   */
  408.                           /* so fudge on the first one, a few more will fit  */
  409. /*
  410.                           listflag = !listflag;
  411. */
  412.                           groupcnt += listflag;
  413. /* Next line replaces trick above to add half ine per list */
  414.                           listflag = 0; 
  415.                           ((struct G_LIST *)objlist)->option = savestr(buf2);
  416.                           valent = NULL;
  417.                           break;
  418.          case TOK_CYCLE:  objlist = newobj(objlist, CLASS_CYCLE, buf1);
  419.                           if (!objlist) return(1);
  420.                           groupcnt++;
  421.                           valent = &((struct G_CYCLE *)objlist)->values;
  422.                           break;
  423.          case TOK_VALUE:  if (valent == NULL) goto error;
  424.                           *valent = get_mem(sizeof(struct G_VALUE));
  425.                           if (!*valent) return(1);
  426.                           (*valent)->next = NULL;
  427.                           (*valent)->title = savestr(buf1);
  428.                           (*valent)->option = savestr(buf2);
  429.                           (*valent)->string = NULL;
  430.                           if (strchr(buf2, '%'))
  431.                           {
  432.                              struct G_STRING *gstr;
  433.  
  434.                              gstr = get_mem(sizeof(struct G_STRING));
  435.                              if (!gstr) return(1);
  436.                              gstr->base.class = CLASS_STRING;
  437.                              (*valent)->string = gstr;
  438.                           }
  439.                           valent = &((*valent)->next);
  440.                           break;
  441.       }
  442.    }
  443.    if (groupcnt > global.maxsize) global.maxsize = groupcnt;
  444.    global.curgroup = global.groups;
  445.    global.menuitem[menupos].nm_Type = MENU_END;
  446.  
  447.    if (toktype == TOK_ERROR)
  448.    {
  449. error:
  450.       sprintf(global.title, "ERROR- %s line %d", cfname, global.line);
  451.       return(1);
  452.    }
  453.    close_config();
  454.  
  455.    /* Now we need to go through the groups and sanitize the pointers as well */
  456.    /* as initialize all the default states                                   */
  457.    init_group(global.objects);
  458.    for(group = global.groups; group != NULL;
  459.        group = (struct G_GROUP *)group->base.next)
  460.    {
  461.       if (group->base.next) group->base.next->prev = (struct G_OBJECT *)group;
  462.       init_group(group->objects);
  463.    }
  464.    return(0);
  465. }
  466.  
  467. /*
  468.  * Open the configuration file and initialize any global data
  469.  */
  470. int init_config(char *cfname)
  471. {
  472.    struct Process *mytask;
  473.    APTR saveptr;
  474.  
  475.    global.state = ST_SCN;
  476.    global.line  = 1;
  477.    mytask = (struct Process *)FindTask(NULL);
  478.    saveptr = mytask->pr_WindowPtr;
  479.    mytask->pr_WindowPtr = (APTR)-1;
  480.    global.fp = fopen(cfname, "r");
  481.    mytask->pr_WindowPtr = saveptr;
  482.    if (global.fp == NULL)
  483.    {
  484.       sprintf(global.title, "ERROR- No %s", cfname);
  485.       return(1);
  486.    }
  487.    return(0);
  488. }
  489.  
  490. /*
  491.  * Close the configuration file and clean up anything else necessary
  492.  */
  493. void close_config()
  494. {
  495.    if (global.fp)
  496.       fclose(global.fp);
  497.    global.fp = NULL;
  498. }
  499.